<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.1.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">
  <meta name="google-site-verification" content="T1Dbmn0YmNZwwxNqq98b7FvVCsxbeIChYP6H75TZjdk">
  <meta name="baidu-site-verification" content="code-86L5xW0tJy">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.14.0/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"www.whatsblockchain.com","root":"/","scheme":"Gemini","version":"8.0.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}};
  </script>

  <meta name="description" content="上一篇我们梳理了一下混币的基本原理，在这一篇中我们开始动手实现一个混币。 混币中最主要的数据结构就是Merkle tree，现在还是借助功德箱来分析如何利用Merkle tree来实现混币功能的。">
<meta property="og:type" content="article">
<meta property="og:title" content="十分钟开发零知识证明之混币-实操篇">
<meta property="og:url" content="https://www.whatsblockchain.com/posts/dfe53fb.html">
<meta property="og:site_name" content="区块链大杂烩">
<meta property="og:description" content="上一篇我们梳理了一下混币的基本原理，在这一篇中我们开始动手实现一个混币。 混币中最主要的数据结构就是Merkle tree，现在还是借助功德箱来分析如何利用Merkle tree来实现混币功能的。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://www.whatsblockchain.com/assets/2019-12-02/mt.png">
<meta property="og:image" content="https://www.whatsblockchain.com/assets/2019-12-02/mt_proof.png">
<meta property="og:image" content="https://www.whatsblockchain.com/assets/2019-12-02/zkp_input.png">
<meta property="og:image" content="https://www.whatsblockchain.com/assets/2019-12-02/send_mixcoin.svg">
<meta property="og:image" content="https://www.whatsblockchain.com/assets/2019-12-02/1.png">
<meta property="og:image" content="https://www.whatsblockchain.com/assets/2019-12-02/2.png">
<meta property="og:image" content="https://www.whatsblockchain.com/assets/2019-12-02/3.png">
<meta property="article:published_time" content="2019-12-02T04:00:00.000Z">
<meta property="article:modified_time" content="2020-09-16T06:53:54.663Z">
<meta property="article:author" content="吴寿鹤">
<meta property="article:tag" content="零知识证明">
<meta property="article:tag" content="隐私币">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://www.whatsblockchain.com/assets/2019-12-02/mt.png">


<link rel="canonical" href="https://www.whatsblockchain.com/posts/dfe53fb.html">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>十分钟开发零知识证明之混币-实操篇 | 区块链大杂烩</title>
  
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-145440251-1"></script>
    <script>
      if (CONFIG.hostname === location.hostname) {
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-145440251-1');
      }
    </script>


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?9af0452214522472f44968f5a652e2f0";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

<!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">区块链大杂烩</h1>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">你想要的这里都有</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <section class="post-toc-wrap sidebar-panel">
          <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Circom"><span class="nav-number">1.</span> <span class="nav-text">Circom</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%94%B5%E8%B7%AF%E8%AE%BE%E8%AE%A1"><span class="nav-number">2.</span> <span class="nav-text">电路设计</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%94%B5%E8%B7%AF%E5%A4%84%E7%90%86"><span class="nav-number">3.</span> <span class="nav-text">电路处理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BC%96%E8%AF%91%E7%94%B5%E8%B7%AF"><span class="nav-number">3.1.</span> <span class="nav-text">编译电路</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#trusted-setup"><span class="nav-number">3.2.</span> <span class="nav-text">trusted setup</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AE%A1%E7%AE%97witness"><span class="nav-number">3.3.</span> <span class="nav-text">计算witness</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%94%9F%E6%88%90%E8%AF%81%E6%98%8E"><span class="nav-number">3.4.</span> <span class="nav-text">生成证明</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%A1%E9%AA%8C%E8%AF%81%E6%98%8E%EF%BC%88%E9%93%BE%E4%B8%8B%E6%A0%A1%E9%AA%8C%EF%BC%89"><span class="nav-number">3.5.</span> <span class="nav-text">校验证明（链下校验）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%A1%E9%AA%8C%E8%AF%81%E6%98%8E%EF%BC%88%E9%93%BE%E4%B8%8A%E6%A0%A1%E9%AA%8C%EF%BC%89"><span class="nav-number">3.6.</span> <span class="nav-text">校验证明（链上校验）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%93%BE%E4%B8%8A%E5%90%88%E7%BA%A6%E8%AE%BE%E8%AE%A1"><span class="nav-number">4.</span> <span class="nav-text">链上合约设计</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AE%9E%E4%BE%8B%E6%BC%94%E7%A4%BA"><span class="nav-number">5.</span> <span class="nav-text">实例演示</span></a></li></ol></div>
      </section>
      <!--/noindex-->

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">吴寿鹤</p>
  <div class="site-description" itemprop="description">代码之美，在于架构，架构之美，在于抽象</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">21</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">11</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



      </section>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">
      

      

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://www.whatsblockchain.com/posts/dfe53fb.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="吴寿鹤">
      <meta itemprop="description" content="代码之美，在于架构，架构之美，在于抽象">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="区块链大杂烩">
    </span>

    
    
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          十分钟开发零知识证明之混币-实操篇
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2019-12-02 12:00:00" itemprop="dateCreated datePublished" datetime="2019-12-02T12:00:00+08:00">2019-12-02</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2020-09-16 14:53:54" itemprop="dateModified" datetime="2020-09-16T14:53:54+08:00">2020-09-16</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E9%9B%B6%E7%9F%A5%E8%AF%86%E8%AF%81%E6%98%8E/" itemprop="url" rel="index"><span itemprop="name">零知识证明</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="阅读次数" id="busuanzi_container_page_pv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="far fa-eye"></i>
      </span>
      <span class="post-meta-item-text">阅读次数：</span>
      <span id="busuanzi_value_page_pv"></span>
    </span>
      </div>
      <div class="post-meta">
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>16k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>14 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <p>上一篇我们梳理了一下混币的基本原理，在这一篇中我们开始动手实现一个混币。</p>
<p>混币中最主要的数据结构就是<code>Merkle tree</code>，现在还是借助功德箱来分析如何利用<code>Merkle tree</code>来实现混币功能的。</p>
<a id="more"></a>
<p>首先回顾一下功德箱的第一个特点：“所有游客向功德箱中放入都是同一年份的一元硬币”，这个特性保证了在功德箱中的硬币都是一模一样的并且无法区分，这一特性是如何在<code>Merkle tree</code>中体现的？先看一下下面这张图，这张图中展示了所有与混币相关的操作，首先看一下如何让游客投入的硬币全部变成一模一样无法区分的硬币，这里用到的是<code>hash</code>算法，<code>hash</code>算法是不可逆的，也就是任何人都无法通过hash后的值推算出原始值是什么。利用<code>hash</code>函数的这一特点就可以将不同的硬币变成无法分辨的同质硬币了。具体操作看下面的图，图中最后一行是原始货币（游客要捐的香火钱），接下来原始货币会被<code>hash</code>函数进行处理变成<code>commitment</code>（混淆之后的货币），<code>commitment</code>会被添加到<code>Merkle tree</code>的叶子节点中，至此利用<code>Merkle tree</code>已经完成了功德箱的第一个特点。</p>
<p><img src="/assets/2019-12-02/mt.png" alt="img"></p>
<p>接下来是功德箱的第二个特性：“使用零知识证明解决在不暴露相关信息的情况下，证明加密货币所有权的问题”。先看第一个问题如何证明加密货币所有权的问题，由于功德箱是通过<code>Merkle tree</code>实现，所以证明是否有某个货币所有权的问题就转化成是否可以证明你拥有<code>Merkle tree</code>上某个叶子节点的<code>preimage</code>（原始货币）同时能给出从这个叶子节点到<code>Merkle tree</code>根之间的路径也就是<code>Merkle path</code>。为了便于理解，接下来通过一个例子来说明，请看下图：</p>
<p><img src="/assets/2019-12-02/mt_proof.png" alt="img"></p>
<p>当小沙弥来到功德箱前，并想取走图中绿色方块（<code>cmt2</code>）中的钱，那么他首先需要提供<code>cmt2</code>的<code>preimage</code>也就是图中的黄色小方块（<code>sk2</code>)，同时要提供一个从<code>cmt2</code>到<code>root</code>之间的<code>Merkle path</code>也就是图中标红的小方块（<code>cmt1</code>,<code>h2</code>)，还有一个<code>Merkle path索引(1,0)</code>索引是<code>(cmt1,h2)</code>在<code>Merkle tree</code>中的位置，索引看用于计算<code>nullifier</code>以及从<code>merkle path</code>计算<code>merkle root</code>。</p>
<p>接下来需要解决的是如何在不暴露隐私的情况下能向他人证明我们拥有<code>cmt2</code>的<code>preimage</code>(<code>sk2</code>)，要解决这个问题就需要零知识证明。请看下面这张图，这张图的作用在上一篇中已经介绍过，这张图演示了零知识证明生成以及校验的过程，在生成零知识证明的时候需要有输入即：<code>Private input</code>，<code>Public Input</code>，然后经过电路运算后会产生一个<code>proof</code>，接下来将<code>proof</code>交给<code>verify</code>校验即可。</p>
<p><img src="/assets/2019-12-02/zkp_input.png" alt="img"></p>
<p>有关电路如何编写将在下个章节中介绍，这里先明确哪些是可以作为<code>public input</code>参数可以公开的，哪些是可以作为<code>private input</code>需要隐藏的。</p>
<p>首先<code>sk2</code>是不能公开的，一旦公开其他人也可以利用<code>sk2</code>进行<code>hash</code>运算生成<code>cmt2</code>并且构造出一个<code>merkle path</code>，然后进行提款。第二个不能公开的是<code>merkle path</code>，因为一旦公开了<code>merkle path</code>，其他人就知道你在<code>merkle tree</code>上的某个叶子节点(<code>cmt2</code>)上取了钱，在结合之前的充钱记录，就可以将发送方与接收方联系起来了，因为向功德箱中放入钱是可以知道谁向<code>Merkle tree</code>上添加了哪个叶子节点的。</p>
<p>所以取款方的<code>Private input</code>为：</p>
<ul>
<li>sk</li>
<li>Merkle path</li>
<li>Merkle path index</li>
</ul>
<p>小沙弥利用<code>Private input</code>结合<code>zk-snark</code>生成的<code>proof</code>可以向公德箱提款了，这里有个问题由于公德箱并没有记录提款记录，那么同一个<code>proof</code>就可以多次提款，为了防止重复提币，还需要<code>nullifier</code>，<code>nullifier</code>是通过<code>sk</code>与<code>Merkle path index</code>进行<code>hash</code>运算产生的，<code>nullifier=hash(sk,merkle_path_index)</code>。另外<code>Merkle tree</code>的<code>root</code>需要公开，不然功德箱没办法校验小沙弥用的<code>Merkle path</code>是不是确实存在于功德箱里的<code>Merkle tree</code>上，小沙弥用一个假的<code>Merkle path</code>也能校验通过。</p>
<p>提款方的<code>public input</code>为：</p>
<ul>
<li>nullifier</li>
<li>root</li>
</ul>
<p>到目前为止，我们利用<code>Merkle tree</code>实现了功德箱的两个特性，接下来就是电路的设计，电路设计使用的<code>Circom</code>。</p>
<h2 id="Circom"><a href="#Circom" class="headerlink" title="Circom"></a>Circom</h2><p><code>Circom</code>是一种用于编写零知识证明的算术电路的语言，<code>Circom</code>简化了创建<code>zk-snark</code>电路的复杂度。<code>Circom</code>有个<code>template</code>关键字，<code>template</code>类似于<code>java</code>面向对象语言中的<code>class</code>，与<code>class</code>一样定义好的模板可以在不同的电路或其他的项目中重用。</p>
<p>下面就是一个模板的<code>example</code>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">template NAND() &#123;</span><br><span class="line">    signal private input a; <span class="comment">// Private input 隐私输入</span></span><br><span class="line">    signal input b; <span class="comment">// public input  公开输入</span></span><br><span class="line">    signal output out;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 约束</span></span><br><span class="line">    out &lt;== <span class="number">1</span> - a*b;</span><br><span class="line">    a*(a<span class="number">-1</span>) === <span class="number">0</span>;</span><br><span class="line">    b*(b<span class="number">-1</span>) === <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">component main = NAND();</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p><code>circom</code>的语法类似于<code>javascript</code>，所以学习的门槛并不是很高，同时<code>circom</code>提供5个额外的操作符用于定义约束。</p>
<p><code>&lt;==,==&gt;</code>操作符用于给<code>signal</code>赋值，同时会生成一个等于约束。</p>
<p><code>&lt;--,--&gt;</code>操作为赋值符号，用于给<code>signal</code>赋值，但不生成任何约束。通常为了强制约束，可以与操作符<code>===</code>一起使用。</p>
<p><code>===</code>操作符定义了等于约束，约束必须简化为<code>a*b+c=0</code>的形式，其中<code>a</code>、<code>b</code>和<code>c</code>是<code>signal</code>的线性组合。</p>
<p>如上面例子中<code>a*(a-1) === 0</code>，<code>b(b-1) === 0</code>强制所有的<code>input</code>必须是<code>0</code>或<code>1</code>。</p>
<h2 id="电路设计"><a href="#电路设计" class="headerlink" title="电路设计"></a>电路设计</h2><p>功德箱的电路就一个目的：“证明你知道<code>merkle tree</code>上某个叶子节点的<code>preimage</code>”。</p>
<p>首先实现一个公用电路<code>GetMerkleRoot</code>,<code>GetMerkleRoot</code>有三个输入参数：</p>
<ul>
<li>sk</li>
<li>Merkle path</li>
<li>Merkle path index</li>
</ul>
<p><code>GetMerkleRoot</code>的目的是对输入的参数进行计算，得出<code>merkle path</code>所在的<code>merkle tree</code>的<code>root</code></p>
<p><code>GetMerkleRoot</code>里使用的<code>hash</code>算法不是<code>sha256</code>而是<code>mimc7</code>，原因是<code>mimc7</code>对电路友好。</p>
<p>以下是<code>GetMerkleRoot</code>的源码：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">include <span class="string">&quot;../circomlib/circuits/mimc.circom&quot;</span>; <span class="comment">//引入 mimc hash算法</span></span><br><span class="line"></span><br><span class="line">template GetMerkleRoot(k)&#123;</span><br><span class="line">    <span class="comment">// k 是Merkle tree 的深度</span></span><br><span class="line"></span><br><span class="line">    signal input leaf; <span class="comment">// 叶子节点 preimage</span></span><br><span class="line">    signal input paths2_root[k]; <span class="comment">// Merkle path</span></span><br><span class="line">    signal input paths2_root_pos[k]; <span class="comment">// Merkle 路径索引</span></span><br><span class="line"></span><br><span class="line">    signal output out;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对Merkle path中前两个元素进行hash运算</span></span><br><span class="line">    component merkle_root[k];</span><br><span class="line">    merkle_root[<span class="number">0</span>] = MiMC7(<span class="number">91</span>);</span><br><span class="line">    merkle_root[<span class="number">0</span>].x_in &lt;== paths2_root[<span class="number">0</span>] - paths2_root_pos[<span class="number">0</span>]* (paths2_root[<span class="number">0</span>] - leaf);</span><br><span class="line">    merkle_root[<span class="number">0</span>].k &lt;== leaf - paths2_root_pos[<span class="number">0</span>]* (leaf - paths2_root[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对Merkle path中剩下的元素进行hash运算</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> v = <span class="number">1</span>; v &lt; k; v++)&#123;</span><br><span class="line">        merkle_root[v] = MiMC7(<span class="number">91</span>);</span><br><span class="line">        merkle_root[v].x_in &lt;== paths2_root[v] - paths2_root_pos[v]* (paths2_root[v] - merkle_root[v<span class="number">-1</span>].out);</span><br><span class="line">        merkle_root[v].k&lt;== merkle_root[v<span class="number">-1</span>].out - paths2_root_pos[v]* (merkle_root[v<span class="number">-1</span>].out - paths2_root[v]);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出计算完成的Merkle tree root</span></span><br><span class="line">    out &lt;== merkle_root[k<span class="number">-1</span>].out;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></p>
<p>接下来是<code>Withdraw</code>电路，<code>withdraw</code>的输入参数分两个部分：</p>
<ul>
<li>public input<ul>
<li>root</li>
<li>nullifierHash</li>
</ul>
</li>
<li>private input<ul>
<li>sk</li>
<li>Merkle path</li>
<li>Merkle path index</li>
</ul>
</li>
</ul>
<p><code>withdraw</code>主要实现以下几个功能：</p>
<ul>
<li>校验<code>sk</code>,<code>merkle path</code>与<code>merkle path index</code>计算出的<code>root</code>与<code>public input</code>中的<code>root</code>是否相等。</li>
<li>校验<code>sk</code>与<code>merkle path index</code>计算出的<code>nullifierHash</code>与<code>public input</code>中的<code>nullifierHash</code>是否相等</li>
</ul>
<p>为什么<code>withdraw</code>电路计算的值需要与<code>public input</code>的参数相等？因为在链上校验<code>proof</code>的时候，合约会将链上的数据（<code>root,nullifierHash</code>）,所以链下的计算会受到链上的约束。</p>
<p>下面是<code>Withdraw</code>电路的源码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">include <span class="string">&quot;./get_merkle_root.circom&quot;</span>; <span class="comment">// 导入 getMerkleRoot 电路</span></span><br><span class="line">include <span class="string">&quot;../circomlib/circuits/mimc.circom&quot;</span>; <span class="comment">//引入 mimc hash算法</span></span><br><span class="line">include <span class="string">&quot;../circomlib/circuits/bitify.circom&quot;</span>;</span><br><span class="line"></span><br><span class="line">template Withdraw(k)&#123;</span><br><span class="line">	<span class="comment">// public input</span></span><br><span class="line">	signal input root; <span class="comment">// Merkle root</span></span><br><span class="line">	signal input nullifierHash;  <span class="comment">// nullifier</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">// private input</span></span><br><span class="line">	signal private input secret; <span class="comment">// 叶子节点 preimage</span></span><br><span class="line"></span><br><span class="line">	signal private input paths2_root[k]; <span class="comment">// merkle path</span></span><br><span class="line">    signal private input paths2_root_pos[k]; <span class="comment">// merkle path index</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">// root constrain</span></span><br><span class="line">	component leaf = MiMC7(<span class="number">91</span>);</span><br><span class="line">	leaf.x_in &lt;== secret;</span><br><span class="line">	leaf.k &lt;== <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    component computed_root = GetMerkleRoot(k);</span><br><span class="line">    computed_root.leaf &lt;== leaf.out;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> w = <span class="number">0</span>; w &lt; k; w++)&#123;</span><br><span class="line">        computed_root.paths2_root[w] &lt;== paths2_root[w];</span><br><span class="line">        computed_root.paths2_root_pos[w] &lt;== paths2_root_pos[w];</span><br><span class="line">    &#125;</span><br><span class="line">    root === computed_root.out;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// nullifier constrain</span></span><br><span class="line">	component cmt_index = Bits2Num(k);</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">var</span> i =<span class="number">0</span> ;i &lt; k ; i++)&#123;</span><br><span class="line">		cmt_index.in[i] &lt;== paths2_root_pos[i];</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	component nullifier = MiMC7(<span class="number">91</span>);</span><br><span class="line">	nullifier.x_in &lt;== cmt_index.out;</span><br><span class="line">	nullifier.k &lt;== secret;</span><br><span class="line"></span><br><span class="line">	nullifierHash === nullifier.out;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">component main = Withdraw(<span class="number">8</span>);</span><br></pre></td></tr></table></figure>
<h2 id="电路处理"><a href="#电路处理" class="headerlink" title="电路处理"></a>电路处理</h2><p>现在所有电路已经全部编写完成了，接下来利用<code>circom</code>做以下几件事：</p>
<ul>
<li>编译电路</li>
<li>计算witness</li>
<li>trusted setup</li>
<li>生成proof</li>
<li>校验proof</li>
</ul>
<p>首先安装<code>Circom</code>与<code>snarkjs</code></p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install -g circom</span><br><span class="line">npm install -g snarkjs</span><br></pre></td></tr></table></figure>
<h3 id="编译电路"><a href="#编译电路" class="headerlink" title="编译电路"></a>编译电路</h3><p>执行以下命令编译电路，编译好的电路会放入<code>circuit.json</code>文件中。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">circom withdraw.circom -o circuit.json</span><br></pre></td></tr></table></figure>
<p>现在电路已经编译完成了，我们可以使用<code>snarkjs printconstraints</code>查看电路约束。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snarkjs printconstraints -c circuit.json</span><br></pre></td></tr></table></figure>
<p>或着使用<code>snarkjs info</code>查看电路的统计信息<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">snarkjs info -c circuit.json</span><br><span class="line"></span><br><span class="line"># Wires: 3674</span><br><span class="line"># Constraints: 3656</span><br><span class="line"># Private Inputs: 17</span><br><span class="line"># Public Inputs: 2</span><br><span class="line"># Outputs: 0</span><br></pre></td></tr></table></figure></p>
<h3 id="trusted-setup"><a href="#trusted-setup" class="headerlink" title="trusted setup"></a>trusted setup</h3><p>使用编译好的电路执行<code>trusted setup</code>，执行完成后会生成两个文件：<code>proving_key.json</code>与<code>verification_key.json</code>。生成证明时需要用到<code>proving key</code>，校验证明时需要用到<code>verification key</code>。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snarkjs setup -c circuit.json --protocol groth</span><br></pre></td></tr></table></figure>
<h3 id="计算witness"><a href="#计算witness" class="headerlink" title="计算witness"></a>计算witness</h3><p>在生成<code>proof</code>之前，需要生成符合电路约束的<code>witness</code>，零知识证明可以证明你知道一个<code>witness</code>，这个<code>witness</code>可以满足电路中所有的约束，但除了<code>public input</code>以及<code>output</code>不会向外泄露任何信息</p>
<p>在生成<code>witness</code>之间需要先将用到的<code>public input</code>以及<code>private input</code>放入到<code>input.json</code>文件中：</p>
<p><code>input.json</code>内容<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;root&quot;: &quot;21150603275199036235447464146889900632582816435445773009431960038115036290869&quot;,</span><br><span class="line">    &quot;nullifierHash&quot;: &quot;8112587267332776847096965636706065951984180935722389598817594570457611916925&quot;,</span><br><span class="line">    &quot;secret&quot;: &quot;0&quot;,</span><br><span class="line">    &quot;paths2_root&quot;: [</span><br><span class="line">        &quot;0&quot;,</span><br><span class="line">        &quot;11730251359286723731141466095709901450170369094578288842486979042586033922425&quot;,</span><br><span class="line">        &quot;9246143820134657901174176070515121907817622693387763521229610032056676659170&quot;,</span><br><span class="line">        &quot;3919701857960328675810908960351443394156342162925591865624975500788003961839&quot;,</span><br><span class="line">        &quot;11868459870544964516983456008242250460119356993157504951373700810334626455267&quot;,</span><br><span class="line">        &quot;17452340833314273101389791943519612073692685328163719737408744891984034913325&quot;,</span><br><span class="line">        &quot;5253775198292439148470029927208469781432760606734473405438165226265735347735&quot;,</span><br><span class="line">        &quot;9586203148669237657308746417333646936338855598595657703565568663564319347700&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;paths2_root_pos&quot;: [</span><br><span class="line">        1,</span><br><span class="line">        1,</span><br><span class="line">        1,</span><br><span class="line">        1,</span><br><span class="line">        1,</span><br><span class="line">        1,</span><br><span class="line">        1,</span><br><span class="line">        1</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>接下来使用<code>snarkjs calculatewitness</code>来生成<code>witness</code>，命令执行完成后会将结果输出到<code>witness.json</code>文件中。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snarkjs calculatewitness -c circuit.json -i input.json</span><br></pre></td></tr></table></figure>
<h3 id="生成证明"><a href="#生成证明" class="headerlink" title="生成证明"></a>生成证明</h3><p>有了<code>witness.json</code>后就可以使用<code>snarkjs proof</code>生成证明</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snarkjs proof -w witness.json --pk proving_key.json</span><br></pre></td></tr></table></figure>
<p>这个命令会使用<code>proving_key.json</code>和<code>witness.json</code>生成<code>proof.json</code>与<code>public.json</code>两个文件,<code>proof.json</code>包含证明相关信息。<code>public.json</code>包含<code>public input</code>以及电路运行后的<code>outputs</code>。</p>
<h3 id="校验证明（链下校验）"><a href="#校验证明（链下校验）" class="headerlink" title="校验证明（链下校验）"></a>校验证明（链下校验）</h3><p>运行<code>snarkjs verify</code>来验证<code>proof</code>是否正确，如果验证通过会输出<code>OK</code>，否则输出<code>INVALID</code><br><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snarkjs verify --vk verification_key.json -p proof.json --pub public.json</span><br></pre></td></tr></table></figure><br>验证<code>proof</code>时会用到<code>verification_key.json</code>，<code>proof.json</code>以及<code>public.json</code>。</p>
<h3 id="校验证明（链上校验）"><a href="#校验证明（链上校验）" class="headerlink" title="校验证明（链上校验）"></a>校验证明（链上校验）</h3><p>为了能够在链上校验<code>proof</code>，需要生成一个链上合约。<code>snarkjs generateverifier</code>会用<code>verification_key.json</code>生成<code>solidity</code>代码并将<code>solidity</code>代码保存到<code>verifier.sol</code>文件中。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snarkjs generateverifier --vk  verification_key.json</span><br></pre></td></tr></table></figure>
<p><code>verifier.sol</code>中包含两个合约：</p>
<ul>
<li>Pairings</li>
<li>Verifier</li>
</ul>
<p>当部署合约时只需要部署<code>Verifier</code>合约即可。<code>Verifier</code>合约部署完成后，可以调用<code>verifyProof</code>方法校验<code>proof</code>，如果校验通过则返回<code>true</code>，否则返回<code>false</code>。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">verifyProof</span>(<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory a,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>][<span class="number">2</span>] memory b,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory c,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory input</span></span></span><br><span class="line"><span class="function"><span class="params">        </span>) <span class="title">public</span> <span class="title">view</span> <span class="title">returns</span> (<span class="params">bool r</span>) </span>&#123;</span><br><span class="line">        Proof memory proof;</span><br><span class="line">        proof.A = Pairing.G1Point(a[<span class="number">0</span>], a[<span class="number">1</span>]);</span><br><span class="line">        proof.B = Pairing.G2Point([b[<span class="number">0</span>][<span class="number">0</span>], b[<span class="number">0</span>][<span class="number">1</span>]], [b[<span class="number">1</span>][<span class="number">0</span>], b[<span class="number">1</span>][<span class="number">1</span>]]);</span><br><span class="line">        proof.C = Pairing.G1Point(c[<span class="number">0</span>], c[<span class="number">1</span>]);</span><br><span class="line">        uint[] memory inputValues = <span class="keyword">new</span> uint[](input.length);</span><br><span class="line">        <span class="keyword">for</span>(uint i = <span class="number">0</span>; i &lt; input.length; i++)&#123;</span><br><span class="line">            inputValues[i] = input[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (verify(inputValues, proof) == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>调用合约方法的参数可以通过<code>snarkjs generatecall</code>生成，<code>snarkjs generatecall</code>会用<code>proof.json</code>与<code>public.json</code>两个文件生成调用<code>Verifier</code>合约所需要的参数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">snarkjs generatecall -p proof.json --pub public.json</span><br><span class="line"></span><br><span class="line">[&quot;0x2e282056742fb4fe24b65531a7e17e66fd2416d293d13a0b4ecc3a4b13be36c5&quot;, &quot;0x22b245ca3c770f9ce2e946ccdd4052b9e13f5292009ea1f212a12b260043aa90&quot;],[[&quot;0x2e2dbd1677ff7712cbc551c63e205d7e5469d52d07f2e4c700fac535378e6d3c&quot;, &quot;0x05f4c0367542bb1281556ff8e773993ed56352dd8cbaa136662d42f959e0ae91&quot;],[&quot;0x2088f897fa5e307f41864ce196e3cb94ed6f574fe594af0b7f3633b695cfb38e&quot;, &quot;0x21f183a915e59a74744a48883c3d03350115897dc3c621c1ab491706350647e1&quot;]],[&quot;0x2166c2e9c9b62f2188a07ea30a9234e6509eaed81a621a93ca7fe000c32fad76&quot;, &quot;0x14eee56356071d21438029846397aefc94fc86d4f3fc521dfb2fbae034c9e049&quot;],[&quot;0x2ec2d13597576e6e9a28d337af768c614a0b892a38aece30dd4df4b1138edf35&quot;,&quot;0x11ef8fc9e658c40fa4a8ae1d40e81084befc8a507f560bb0f2c33bb14cca567d&quot;]</span><br></pre></td></tr></table></figure>
<h2 id="链上合约设计"><a href="#链上合约设计" class="headerlink" title="链上合约设计"></a>链上合约设计</h2><p>电路生成好后接下来就到了最后一个环节链上合约设计，链上合约一共有两个：</p>
<ul>
<li>Merkle Tree</li>
<li>Mixer</li>
</ul>
<p><code>merkle tree</code>合约主要负责：</p>
<ul>
<li>维护<code>merkle tree</code>数据结构</li>
<li>生成<code>merkle proof</code></li>
</ul>
<p><code>Merkle tree</code>合约源码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line">pragma solidity ^<span class="number">0.5</span><span class="number">.0</span>;</span><br><span class="line"></span><br><span class="line">contract IMimc &#123; <span class="comment">// Mimc hash函数</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">MiMCpe7</span>(<span class="params">uint256 in_x,uint256 in_k</span>) <span class="title">public</span> <span class="title">returns</span>(<span class="params">uint256 out_x</span>);</span></span><br><span class="line"><span class="function">&#125;</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">contract</span> <span class="title">MerkelTree</span> </span>&#123;</span><br><span class="line">    mapping (<span class="function"><span class="params">uint256</span> =&gt;</span> bool) public roots; <span class="comment">// 记录Merkle tree上的root</span></span><br><span class="line">    uint public tree_depth = <span class="number">8</span>; <span class="comment">// Merkle tree 深度</span></span><br><span class="line">    uint public no_leaves = <span class="number">256</span>; <span class="comment">// Merkle tree 叶子节点个数</span></span><br><span class="line">    struct Mtree &#123;</span><br><span class="line">        uint256 cur;</span><br><span class="line">        uint256[<span class="number">256</span>][<span class="number">9</span>] leaves2; <span class="comment">// tree depth + 1</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Mtree public MT;</span><br><span class="line"></span><br><span class="line">    IMimc mimc;</span><br><span class="line"></span><br><span class="line">    event LeafAdded(uint256 index);</span><br><span class="line"></span><br><span class="line">    event TestMimc(uint256);</span><br><span class="line"></span><br><span class="line">    event MerkleProof(uint256[<span class="number">8</span>] , uint256[<span class="number">8</span>] );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(address _mimc) public&#123;</span><br><span class="line">        mimc = IMimc(_mimc);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//Merkletree.append(com)</span></span><br><span class="line">    <span class="comment">// 插入 commitment叶子节点</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">insert</span>(<span class="params">uint256 com</span>) <span class="title">public</span> <span class="title">returns</span> (<span class="params">uint256 </span>) </span>&#123;</span><br><span class="line">        <span class="built_in">require</span> (MT.cur != no_leaves );</span><br><span class="line">        MT.leaves2[<span class="number">0</span>][MT.cur] = com;</span><br><span class="line">        updateTree();</span><br><span class="line">        emit LeafAdded(MT.cur);</span><br><span class="line">        MT.cur++;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> MT.cur<span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回Merkle path，Merkle path index</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">getMerkelProof</span>(<span class="params">uint256 index</span>) <span class="title">public</span> <span class="title">returns</span> (<span class="params">uint256[<span class="number">8</span>] memory, uint256[<span class="number">8</span>] memory</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">        uint256[<span class="number">8</span>] memory address_bits;</span><br><span class="line">        uint256[<span class="number">8</span>] memory merkelProof;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (uint256 i=<span class="number">0</span> ; i &lt; tree_depth; i++) &#123;</span><br><span class="line">            <span class="comment">// address_bits[i] = index%2;</span></span><br><span class="line">            <span class="keyword">if</span> (index%<span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">                address_bits[i]=<span class="number">1</span>;</span><br><span class="line">                merkelProof[i] = getUniqueLeaf(MT.leaves2[i][index + <span class="number">1</span>],i);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                address_bits[i]=<span class="number">0</span>;</span><br><span class="line">                merkelProof[i] = getUniqueLeaf(MT.leaves2[i][index - <span class="number">1</span>],i);</span><br><span class="line">            &#125;</span><br><span class="line">            index = uint256(index/<span class="number">2</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        emit MerkleProof(merkelProof, address_bits);</span><br><span class="line">        <span class="keyword">return</span>(merkelProof, address_bits);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">getMimc</span>(<span class="params">uint256 input, uint256 sk</span>) <span class="title">public</span> <span class="title">returns</span> (<span class="params"> uint256</span>) </span>&#123;</span><br><span class="line">        emit TestMimc(mimc.MiMCpe7(input , sk));</span><br><span class="line">        <span class="keyword">return</span> mimc.MiMCpe7(input , sk);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">getUniqueLeaf</span>(<span class="params">uint256 leaf, uint256 depth</span>) <span class="title">public</span> <span class="title">returns</span> (<span class="params">uint256</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (leaf == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (uint256 i=<span class="number">0</span>;i&lt;depth;i++) &#123;</span><br><span class="line">                leaf = mimc.MiMCpe7(leaf, leaf);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (leaf);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 更新Merkle  tree</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">updateTree</span>(<span class="params"></span>) <span class="title">public</span> <span class="title">returns</span>(<span class="params">uint256 root</span>) </span>&#123;</span><br><span class="line">        uint256 CurrentIndex = MT.cur;</span><br><span class="line">        uint256 leaf1;</span><br><span class="line">        uint256 leaf2;</span><br><span class="line">        <span class="keyword">for</span> (uint256 i=<span class="number">0</span> ; i &lt; tree_depth; i++) &#123;</span><br><span class="line">            uint256 NextIndex = CurrentIndex/<span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span> (CurrentIndex%<span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">                leaf1 =  MT.leaves2[i][CurrentIndex];</span><br><span class="line">                leaf2 = getUniqueLeaf(MT.leaves2[i][CurrentIndex + <span class="number">1</span>], i);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                leaf1 = getUniqueLeaf(MT.leaves2[i][CurrentIndex - <span class="number">1</span>], i);</span><br><span class="line">                leaf2 =  MT.leaves2[i][CurrentIndex];</span><br><span class="line">            &#125;</span><br><span class="line">            MT.leaves2[i+<span class="number">1</span>][NextIndex] = mimc.MiMCpe7( leaf1, leaf2);</span><br><span class="line">            CurrentIndex = NextIndex;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> MT.leaves2[tree_depth][<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">getLeaf</span>(<span class="params">uint256 j,uint256 k</span>) <span class="title">public</span> <span class="title">view</span> <span class="title">returns</span> (<span class="params">uint256 root</span>) </span>&#123;</span><br><span class="line">        root = MT.leaves2[j][k];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回Merkle tree root</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">getRoot</span>(<span class="params"></span>) <span class="title">public</span> <span class="title">view</span> <span class="title">returns</span>(<span class="params">uint256 root</span>) </span>&#123;</span><br><span class="line">        root = MT.leaves2[tree_depth][<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p><code>Mixer</code>合约主要负责：</p>
<ul>
<li>记录所有合法<code>Merkle tree root</code>，防止链下造假</li>
<li>记录已使用的<code>nullifierHash</code>，防止双花</li>
<li>记录<code>commitment</code>，确保同一个<code>commitment</code>不会多次被添加到<code>Merkle tree</code>上</li>
<li><code>deposit</code>方法，用户调用<code>deposit</code>时，向<code>Mixer</code>中存储0.01以太币，同时会将<code>commitment</code>添加到<code>merkle tree</code>上</li>
<li><code>withdraw</code>方法，用户调用<code>withdraw</code>时，<code>Mixer</code>校验<code>proof</code>是否合法，如果合法则给用户转0.01以太币</li>
<li><code>forward</code>方法，用户调用<code>forward</code>时，<code>Mixer</code>校验<code>proof</code>是否合法，如何合法则在<code>merkle tree</code>中插入一个新的<code>commitment</code></li>
</ul>
<p>当<code>merkle tree</code>是空时，所有的<code>commitment</code>都是初始化状态”0x0”，<code>Alice</code>向<code>merkle tree</code>中添加了一个<code>commitment</code>后，<code>Bob</code>从合约中用<code>secret</code>将这个<code>commitment</code>所表示的币给提走了，那么第三方很容易将<code>Alice</code>与<code>Bob</code>联系起来，从这里也可以看出当越多的人使用<code>mixer</code>时，<code>mixer</code>的隐私性越好，反之则越差。</p>
<p>那么在使用人数较少的时候如何保证隐私？当<code>Bob</code>接受到<code>Alice</code>的转账，<code>Bob</code>向将币转给<code>Carl</code>那么<code>Bob</code>可以调用<code>forward</code>方法，具体流程如下：</p>
<ul>
<li><code>Bob</code>向<code>Mixer</code>提供<code>proof</code>证明他可以消费某一个币，同时提交一个新的<code>commitment</code>这个<code>commitment</code>是<code>Carl</code>可以消费的</li>
<li><code>forward</code>校验<code>Bob</code>的<code>proof</code>是否正确，如何正确则向<code>merkle tree</code>中插入新的<code>commitment</code></li>
<li><code>forawrd</code>在合约中作废<code>Bob</code>已经消费的<code>nulliferHash</code></li>
</ul>
<p><code>Mixer</code>合约源码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line">pragma solidity ^<span class="number">0.5</span><span class="number">.0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;./MerkleTree.sol&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;./verifier.sol&quot;</span>;</span><br><span class="line"></span><br><span class="line">contract Mixer is MerkelTree ,Verifier &#123;</span><br><span class="line">    mapping(<span class="function"><span class="params">uint256</span> =&gt;</span> bool) public roots; <span class="comment">// 记录所有合法的Merkle tree root</span></span><br><span class="line">    mapping(<span class="function"><span class="params">uint256</span> =&gt;</span> bool) public nullifierHashes; <span class="comment">// 记录已使用的nullifier，防止双花</span></span><br><span class="line">    mapping(<span class="function"><span class="params">uint256</span> =&gt;</span> bool) public commitments; <span class="comment">// 记录commitment，同一个commitment不允许放入两次</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 每次只能向mixer中投入0.01个以太币</span></span><br><span class="line">    uint256 constant public AMOUNT = <span class="number">0.01</span> ether;</span><br><span class="line"></span><br><span class="line">    event Deposit(uint256 indexed commitment, uint256 leafIndex, uint256 timestamp);</span><br><span class="line">    event Withdraw(address to, uint256 nullifierHash);</span><br><span class="line">    event Forward(uint256 indexed commitment, uint256 leafIndex, uint256 timestamp);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Constructor</span></span><br><span class="line">    <span class="keyword">constructor</span>  (address _mimc) MerkelTree(_mimc) public &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 向mixer中存入0.01个以太坊，同时将_commitment插入到Merkle tree中</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">deposit</span> (<span class="params">uint256 _commitment</span>) <span class="title">payable</span> <span class="title">public</span></span>&#123;</span><br><span class="line">        <span class="built_in">require</span>(!commitments[_commitment], <span class="string">&quot;The commitment has been submitted&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">require</span>(msg.value == AMOUNT);</span><br><span class="line">        uint256 insertedIndex = insert(_commitment);</span><br><span class="line">        commitments[_commitment] = <span class="literal">true</span>;</span><br><span class="line">        roots[getRoot()] = <span class="literal">true</span>;</span><br><span class="line">        emit Deposit(_commitment,insertedIndex,block.timestamp);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 向mixer提交proof，校验通过后可以提走0.01个以太币</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">withdraw</span>(<span class="params">uint[<span class="number">2</span>] memory a,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>][<span class="number">2</span>] memory b,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory c,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory input</span>) <span class="title">public</span> <span class="title">payable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        uint256 _nullifierHash = uint256(input[<span class="number">1</span>]);</span><br><span class="line">        uint256 _root = uint256(input[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">require</span>(!nullifierHashes[_nullifierHash], <span class="string">&quot;The note has been already spent&quot;</span>);</span><br><span class="line">        <span class="built_in">require</span>(isKnownRoot(_root), <span class="string">&quot;Cannot find your merkle root&quot;</span>);</span><br><span class="line">        <span class="built_in">require</span>(verifyProof(a,b,c,input), <span class="string">&quot;Invalid withdraw proof&quot;</span>);</span><br><span class="line"></span><br><span class="line">        nullifierHashes[_nullifierHash] = <span class="literal">true</span>;</span><br><span class="line">        msg.sender.transfer(AMOUNT); <span class="comment">// 处理转账</span></span><br><span class="line">        emit Withdraw(msg.sender, _nullifierHash);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// forward 允许用户直接在合约中进行转账操作</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">forward</span> (<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory a,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>][<span class="number">2</span>] memory b,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory c,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint[<span class="number">2</span>] memory input,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint256 _commitment</span></span></span><br><span class="line"><span class="function"><span class="params">    </span>) <span class="title">public</span> <span class="title">returns</span> (<span class="params">address</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">        uint256 _nullifierHash = uint256(input[<span class="number">1</span>]);</span><br><span class="line">        uint256 _root = uint256(input[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">require</span>(!commitments[_commitment], <span class="string">&quot;The commitment has been submitted&quot;</span>);</span><br><span class="line">        <span class="built_in">require</span>(!nullifierHashes[_nullifierHash], <span class="string">&quot;The note has been already spent&quot;</span>);</span><br><span class="line">        <span class="built_in">require</span>(isKnownRoot(_root), <span class="string">&quot;Cannot find your merkle root&quot;</span>); <span class="comment">// Make sure to use a recent one</span></span><br><span class="line">        <span class="built_in">require</span>(verifyProof(a,b,c,input), <span class="string">&quot;Invalid withdraw proof&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        uint insertedIndex = insert(_commitment);</span><br><span class="line">        roots[getRoot()] = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">        nullifierHashes[_nullifierHash] = <span class="literal">true</span>;</span><br><span class="line">        emit Forward(_commitment,insertedIndex,block.timestamp);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">isKnownRoot</span>(<span class="params">uint256 _root</span>) <span class="title">public</span> <span class="title">returns</span>(<span class="params">bool</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> roots[_root];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>到这里已经将实现混币的所有步骤都分享完了，下面演示一下怎么使用<code>Mixer</code>。</p>
<h2 id="实例演示"><a href="#实例演示" class="headerlink" title="实例演示"></a>实例演示</h2><p>我在<code>ropsten</code>上部署已经部署好一个<code>Mxier</code>合约，合约地址：”<a target="_blank" rel="noopener" href="https://ropsten.etherscan.io/address/0x46a7f914785357b9054fdb670845dc6c0c968167">https://ropsten.etherscan.io/address/0x46a7f914785357b9054fdb670845dc6c0c968167</a>“</p>
<p>下图演示了使用<code>Mixer</code>的流程，例如：<code>Alice</code>要给<code>Bob</code>转0.01ether，那么会涉及到以下几个步骤：</p>
<ol>
<li><code>Bob</code>在本地产生一个随机数<code>sk2</code>，然后将<code>sk2</code>用<code>mimc7</code>进行<code>hash</code>运算得到<code>cmt2</code>，然后将<code>cmt2</code>发送给<code>Alice</code></li>
<li><code>Alice</code>调用<code>Mixer</code>合约<code>deposit</code>方法，并传入<code>cmt2</code>参数</li>
<li><code>Bob</code>接收到<code>Alice</code>转账时，可以在本地生成<code>proof</code>，调用<code>Mixer</code>的<code>withdraw</code>方法取回0.01 ether。</li>
<li><code>Mixer</code>校验<code>proof</code>通过后，向<code>Bob</code>转0.01 ether</li>
</ol>
<p><img src="/assets/2019-12-02/send_mixcoin.svg" alt="img"></p>
<p>下图是我在<code>ropsten</code>上先存储0.01 ether，然后在取回0.01 ether</p>
<p><img src="/assets/2019-12-02/1.png" alt="img"></p>
<p>下图是在<code>ropsten</code>上调用<code>Mixer</code>合约<code>deposit</code>方法的交易记录：<br><img src="/assets/2019-12-02/2.png" alt="img"></p>
<p>下图是在<code>ropsten</code>上调用<code>Mixer</code>合约<code>withdraw</code>方法的交易记录：<br><img src="/assets/2019-12-02/3.png" alt="img"></p>
<p>终于写完了<code>^_^</code>，需要源码的小伙伴可以关注公众号。</p>

    </div>

    
    
    
      
  <div class="popular-posts-header">相关文章推荐</div>
  <ul class="popular-posts">
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="/posts/11d5a5c5.html" rel="bookmark">十分钟开发零知识证明之混币--原理篇</a></div>
    </li>
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="/posts/318c223a.html" rel="bookmark">zkRollup介绍 原理篇 </a></div>
    </li>
    <li class="popular-posts-item">
      <div class="popular-posts-title"><a href="/posts/58cf1c1b.html" rel="bookmark">零知识证明介绍 </a></div>
    </li>
  </ul>


    <footer class="post-footer">
          

<div class="post-copyright">
<ul>
  <li class="post-copyright-author">
      <strong>原作者： </strong>吴寿鹤
  </li>
  <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="https://www.whatsblockchain.com/posts/dfe53fb.html" title="十分钟开发零知识证明之混币-实操篇">https://www.whatsblockchain.com/posts/dfe53fb.html</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>

          <div class="post-tags">
              <a href="/tags/%E9%9B%B6%E7%9F%A5%E8%AF%86%E8%AF%81%E6%98%8E/" rel="tag"># 零知识证明</a>
              <a href="/tags/%E9%9A%90%E7%A7%81%E5%B8%81/" rel="tag"># 隐私币</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/posts/11d5a5c5.html" rel="prev" title="十分钟开发零知识证明之混币--原理篇">
                  <i class="fa fa-chevron-left"></i> 十分钟开发零知识证明之混币--原理篇
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/posts/3e17fcd9.html" rel="next" title="科普：什么是以太坊">
                  科普：什么是以太坊 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
  
  
  



      

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

    </div>
  </main>

  <footer class="footer">
    <div class="footer-inner">
      

      

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">吴寿鹤</span>
</div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">87k</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">1:19</span>
  </span>
</div>
<div class="busuanzi-count">
    <span class="post-meta-item" id="busuanzi_container_site_uv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-user"></i>
      </span>
      <span class="site-uv" title="总访客量">
        <span id="busuanzi_value_site_uv"></span>
      </span>
    </span>
    <span class="post-meta-item" id="busuanzi_container_site_pv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-eye"></i>
      </span>
      <span class="site-pv" title="总访问量">
        <span id="busuanzi_value_site_pv"></span>
      </span>
    </span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.0/lib/anime.min.js"></script>
<script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script>

  
  <script>
    (function(){
      var bp = document.createElement('script');
      var curProtocol = window.location.protocol.split(':')[0];
      bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(bp, s);
    })();
  </script>


















  
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>








  

  
      <script>
  if (typeof MathJax === 'undefined') {
    window.MathJax = {
      tex: {
        inlineMath: {'[+]': [['$', '$']]},
        tags: 'ams'
      },
      options: {
        renderActions: {
          findScript: [10, doc => {
            document.querySelectorAll('script[type^="math/tex"]').forEach(node => {
              const display = !!node.type.match(/; *mode=display/);
              const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
              const text = document.createTextNode('');
              node.parentNode.replaceChild(text, node);
              math.start = {node: text, delim: '', n: 0};
              math.end = {node: text, delim: '', n: 0};
              doc.math.push(math);
            });
          }, '', false],
          insertedScript: [200, () => {
            document.querySelectorAll('mjx-container').forEach(node => {
              const target = node.parentNode;
              if (target.nodeName.toLowerCase() === 'li') {
                target.parentNode.classList.add('has-jax');
              }
            });
          }, '', false]
        }
      }
    };
    const script = document.createElement('script');
    script.src = '//cdn.jsdelivr.net/npm/mathjax@3.1.0/es5/tex-mml-chtml.js';
    script.defer = true;
    document.head.appendChild(script);
  } else {
    MathJax.startup.document.state(0);
    MathJax.typesetClear();
    MathJax.texReset();
    MathJax.typeset();
  }
</script>

    

  

</body>
</html>
